#include <stdio.h>
#include "Nano100Series.h"
#include "Common.h"
#include "Edid.h"
#include "BrgHdmiMipi.h"
#include "DispModule.h"
#include "Config.h"
#include "ProximitySensor.h"
#include "SysTick.h"
#include "MotionSensor.h"
#include "UsbTransfer.h"
#include "TouchSensor.h"
#include "Input.h"
#include "EventQueue.h"
#include "I2c0.h"
#include "I2c1.h"
#include "GpioI2c2.h"
#include "Timer1.h"
#include "GpioIrq.h"

const uint32_t disp_module_backlight_tab[] = {
	//5, 7, 9, 12, 15, 19, 23, 27, 32, 37, 43, 49, 56, 64, 75, 86, 100,
	5, 9, 15, 18, 23, 28, 32, 43, 56, 70, 100,
};

#define DISP_MODULE_BACKLIGHT_TAB_SIZE (sizeof(disp_module_backlight_tab)/ sizeof(disp_module_backlight_tab[0]))
uint32_t disp_module_backlight_index = DISP_MODULE_BACKLIGHT_TAB_SIZE - 1;

void SystemCoreInit(void)
{
    SYS_UnlockReg();

    /* 12MHz HXT, HCLK from PLL */ 
    CLK_EnableXtalRC(CLK_PWRCTL_HXT_EN_Msk);
    CLK_EnablePLL(CLK_PLLCTL_PLL_SRC_HXT, 96000000);
    CLK_WaitClockReady(CLK_CLKSTATUS_HXT_STB_Msk | CLK_CLKSTATUS_PLL_STB_Msk);
    CLK_SetHCLK(CLK_CLKSEL0_HCLK_S_PLL, CLK_HCLK_CLK_DIVIDER(3));

    /* to calculate PllClock, SystemCoreClock and CycylesPerUs automatically. */
    SystemCoreClockUpdate();
		
    SYS_LockReg();
}

void SystemPeripheralInit(void)
{
	SYS_UnlockReg();

	/* UART0 : Debug */
	CLK_SetModuleClock(UART0_MODULE, CLK_CLKSEL1_UART_S_HXT, CLK_UART_CLK_DIVIDER(1));
	CLK_EnableModuleClock(UART0_MODULE);
	SYS->PB_L_MFP &= ~(SYS_PB_L_MFP_PB0_MFP_Msk | SYS_PB_L_MFP_PB1_MFP_Msk);
    SYS->PB_L_MFP |= (SYS_PB_L_MFP_PB0_MFP_UART0_RX | SYS_PB_L_MFP_PB1_MFP_UART0_TX);

	/* I2C0  : hdmi to mipi bridge */
	CLK_SetModuleClock(I2C0_MODULE, 0, 0);
	CLK_EnableModuleClock(I2C0_MODULE);
	SYS->PA_L_MFP &= ~(SYS_PA_L_MFP_PA5_MFP_Msk | SYS_PA_L_MFP_PA4_MFP_Msk);
	SYS->PA_L_MFP |= (SYS_PA_L_MFP_PA5_MFP_I2C0_SCL | SYS_PA_L_MFP_PA4_MFP_I2C0_SDA);
	I2c0Init();

	/* I2C1  : proximity sensor, motion sensor */
	CLK_SetModuleClock(I2C1_MODULE, 0, 0);
	CLK_EnableModuleClock(I2C1_MODULE);
	SYS->PA_H_MFP &= ~(SYS_PA_H_MFP_PA11_MFP_Msk | SYS_PA_H_MFP_PA10_MFP_Msk);
	SYS->PA_H_MFP |= (SYS_PA_H_MFP_PA11_MFP_I2C1_SCL | SYS_PA_H_MFP_PA10_MFP_I2C1_SDA);
	I2c1Init();

	/* GPIO I2C2 : LCD Power */
	GPIO_SetMode(I2C2_SDA_PORT, I2C2_SDA_BIT, GPIO_PMD_INPUT);
	GPIO_SetMode(I2C2_SCL_PORT, I2C2_SCL_BIT, GPIO_PMD_INPUT);
	GpioI2c2Init();

	/* USB */
	CLK_SetModuleClock(USBD_MODULE, 0, CLK_USB_CLK_DIVIDER(2));
    CLK_EnableModuleClock(USBD_MODULE);

	/* PWM0 CH1 for LCD backlight */
	CLK_EnableModuleClock(PWM0_CH01_MODULE);
	CLK_SetModuleClock(PWM0_CH01_MODULE, (0x2UL<<4), 0);
	SYS->PC_L_MFP &= ~SYS_PC_L_MFP_PC7_MFP_Msk;
	SYS->PC_L_MFP |= SYS_PC_L_MFP_PC7_MFP_PWM0_CH1;

	/* TIMER0 for counter */
	CLK_EnableModuleClock(TMR0_MODULE);
	CLK_SetModuleClock(TMR0_MODULE, CLK_CLKSEL1_TMR0_S_HXT, 0);

	/* TIMER1 for motion sensor and proximity sensor */
	CLK_EnableModuleClock(TMR1_MODULE);
	CLK_SetModuleClock(TMR1_MODULE, CLK_CLKSEL1_TMR1_S_HXT, 0);
	
	/* TIMER2 for touch sensor */
	CLK_EnableModuleClock(TMR2_MODULE);
	CLK_SetModuleClock(TMR2_MODULE, CLK_CLKSEL2_TMR2_S_HXT, 0);

	/* GPIO */
	GPIO_SetMode(DISP_MODULE_RST_PORT,      DISP_MODULE_RST_BIT,      GPIO_PMD_OUTPUT);
	GPIO_SetMode(DISP_MODULE_PWR_PORT,      DISP_MODULE_PWR_BIT,      GPIO_PMD_OUTPUT);

	GPIO_SetMode(BRG_HDMI_MIPI_RST_PORT,    BRG_HDMI_MIPI_RST_BIT,    GPIO_PMD_OUTPUT);
	GPIO_SetMode(BRG_HDMI_MIPI_AUDIO_PORT,  BRG_HDMI_MIPI_AUDIO_BIT,  GPIO_PMD_OUTPUT);
	BRG_HDMI_MIPI_AUDIO_PIN = 0;

	GPIO_SetMode(PROXIMITY_SENSOR_INI_PORT, PROXIMITY_SENSOR_INI_BIT, GPIO_PMD_INPUT);

	GPIO_SetMode(TOUCH_SENSOR_LEFT_KEY0_PORT, TOUCH_SENSOR_LEFT_KEY0_BIT, GPIO_PMD_INPUT);
	GPIO_SetMode(TOUCH_SENSOR_LEFT_KEY1_PORT, TOUCH_SENSOR_LEFT_KEY1_BIT, GPIO_PMD_INPUT);
	GPIO_SetMode(TOUCH_SENSOR_LEFT_KEY2_PORT, TOUCH_SENSOR_LEFT_KEY2_BIT, GPIO_PMD_INPUT);
	GPIO_SetMode(TOUCH_SENSOR_LEFT_KEY3_PORT, TOUCH_SENSOR_LEFT_KEY3_BIT, GPIO_PMD_INPUT);

	GPIO_SetMode(TOUCH_SENSOR_RIGHT_KEY0_PORT, TOUCH_SENSOR_RIGHT_KEY0_BIT, GPIO_PMD_INPUT);
	GPIO_SetMode(TOUCH_SENSOR_RIGHT_KEY1_PORT, TOUCH_SENSOR_RIGHT_KEY1_BIT, GPIO_PMD_INPUT);
	GPIO_SetMode(TOUCH_SENSOR_RIGHT_KEY2_PORT, TOUCH_SENSOR_RIGHT_KEY2_BIT, GPIO_PMD_INPUT);
	GPIO_SetMode(TOUCH_SENSOR_RIGHT_KEY3_PORT, TOUCH_SENSOR_RIGHT_KEY3_BIT, GPIO_PMD_INPUT);

	SYS_EnableBOD(SYS_BODCTL_BOD25_RST_EN_Msk, SYS_BODCTL_BOD25_EN_Msk);

	SYS_LockReg();
}

void BrightnessPlusHandle(void)
{
	int i;

	InputReportKeyAuto(&hmd_event_queue, KEY_BRIGHTNESSUP);

	if (disp_module_backlight_index < (DISP_MODULE_BACKLIGHT_TAB_SIZE - 1)) {
		disp_module_backlight_index += 1;
	}

	brightness_set_by_key = disp_module_backlight_tab[disp_module_backlight_index];
	is_brightness_changed_by_key = 1;

	for (i = 0; i < brightness_set_by_key; i++) {
		LogDebug("|");
	}
	LogDebug("\n");
}

void BrightnessMinusHandle(void)
{
	int i;

	InputReportKeyAuto(&hmd_event_queue, KEY_BRIGHTNESSDOWN);

	if (disp_module_backlight_index > 0) {
		disp_module_backlight_index -= 1;
	}

	brightness_set_by_key = disp_module_backlight_tab[disp_module_backlight_index];
	is_brightness_changed_by_key = 1;

	for (i = 0; i < brightness_set_by_key; i++) {
		LogDebug("|");
	}
	LogDebug("\n");
}

void VolumePlusHandle(void)
{
	int i;

	InputReportKeyAuto(&hmd_event_queue, KEY_VOLUMEUP);

	volume_set_by_key++;
	for (i = 0; i < volume_set_by_key; i++) {
		LogDebug("#");
	}
	LogDebug("\n");
}

void VolumeMinusHandle(void)
{
	int i;

	InputReportKeyAuto(&hmd_event_queue, KEY_VOLUMEDOWN);

	volume_set_by_key--;
	if (volume_set_by_key < 0) {
		volume_set_by_key = 0;
	}
	for (i = 0; i < volume_set_by_key; i++) {
		LogDebug("#");
	}
	LogDebug("\n");
}

void EventQueueDump(void)
{
	int ret;
	struct event_t key_event;

	do {
		TouchSensorDisable();
		ret = EventQueueDeQueue(&hmd_event_queue, &key_event);
		TouchSensorEnable();
		if (ret == 0) {
			LogDebug("event type : %d, code : %d, value : %d\n", key_event.type, key_event.code, key_event.value);
		}
	} while (ret == 0);

}

void CalculateDispModuleBacklightIndex(uint32_t level, uint32_t *index)
{
	int i;

	for (i = 0; i < (DISP_MODULE_BACKLIGHT_TAB_SIZE - 1); i++) {
		if ((level >= disp_module_backlight_tab[i])
			&& (level < disp_module_backlight_tab[i + 1]))
		{
			*index = i;
			break;
		}
	}
	if (i >= (DISP_MODULE_BACKLIGHT_TAB_SIZE - 1)) {
		*index = DISP_MODULE_BACKLIGHT_TAB_SIZE - 1;
	}
}

int main(void)
{
	enum proximity_sensor_state_t proximity_sensor_state;
	uint32_t is_hdmi_input_active = 0;
	uint32_t proximity_sensor_leave_counter_old;
	uint32_t proximity_sensor_leave_prepare = 0;
	uint32_t is_ever_on = 0;

	SystemCoreInit();
	SystemPeripheralInit();

	UART_Open(UART0, 115200);
	LogDebug("\nD1 HMD power on ...\n");

	SysConfigInit();
	SysConfigLoad(&sys_config);
	SysConfigDump(&sys_config);
	SysTickInit();
	EdidInit();
	DispModuleInit();
	BrgHdmiMipiInit();
	ProximitySensorInit();
	MotionSensorInit();
	EventQueueInit(&hmd_event_queue, &(hmd_events[0]), HMD_EVENT_SIZE);
	UsbTransferInit();
	GpioIrqInit();

	CalculateDispModuleBacklightIndex(sys_config.brightness, &disp_module_backlight_index);
	BrgHdmiMipiSetVideoTiming(VIDEO_TIMING_1440P);
	UsbTransferStart();
	Timer1Open();

	while (1) {
		// handle USB event
		UsbTransferHandleEvent();

		// update sensor
		if (Timer1IsTrigger()) {
			Timer1ResetFlag();
			ProximitySensorUpdateState();
			UsbTransferHandleSensorUpdate();
		}

		// get new state
		is_hdmi_input_active = BrgHdmiMipiIsHdmiActive();
		proximity_sensor_state = ProximitySensorGetState();

		// turn on display immediately
		if ((is_hdmi_input_active && (proximity_sensor_state == PROXIMITY_SENSOR_STATE_CLOSE))
			|| (is_hdmi_input_active && (is_ever_on == 0)))
		{
			is_ever_on = 1;
			if (!is_video_on) {
				int i;
				LogDebug("Turn on display\n");
				DispModuleSetVideoTiming(VIDEO_TIMING_1440P);
				DispModuleDisplayOn();
				BrgHdmiMipiSetMipiOn();
				for (i = 0; i <= sys_config.brightness; i++) {
					DispModuleSetBacklightLevel(i);
					DelayMs(7);
				}
				DelayMs(100);
				BrgHdmiMipiSetAudioOn();
				is_video_on = 1;	
				proximity_sensor_leave_prepare = 0;
			}
		}

		// turn off display immediately
		if (!is_hdmi_input_active) {
			if (is_video_on) {
				LogDebug("Turn off display wiht no hdmi input\n");
				BrgHdmiMipiSetAudioOff();
				DelayMs(100);
				DispModuleSetBacklightLevel(0);
				BrgHdmiMipiSetVideoTiming(VIDEO_TIMING_1440P);
				DispModuleDisplayOff();
				is_video_on = 0;
				DelayMs(100);
			}
		}

		// turn off display with debounce of x seconds
		if (proximity_sensor_state == PROXIMITY_SENSOR_STATE_LEAVE) {
			if (is_video_on) {
				if (proximity_sensor_leave_prepare) {
					if ((SysTickGet() - proximity_sensor_leave_counter_old) > 5) {
						int i;
						LogDebug("Turn off display wiht PROXIMITY_SENSOR_STATE_LEAVE\n");
						BrgHdmiMipiSetAudioOff();
						DelayMs(100);
						for (i = sys_config.brightness; i >= 0; i--) {
							DispModuleSetBacklightLevel(i);
							DelayMs(1);
						}
						BrgHdmiMipiSetVideoTiming(VIDEO_TIMING_1440P);
						DispModuleDisplayOff();
						is_video_on = 0;
						DelayMs(100);
					}
				} else {
					proximity_sensor_leave_prepare = 1;	// enter prepare state
					proximity_sensor_leave_counter_old = SysTickGet();
				}
			}
		}

		// adjust brightness
		if (is_video_on ) {
			if (is_brightness_changed_by_cmd) {
				sys_config.brightness = brightness_set_by_cmd;
				SysConfigStore(&sys_config);
				DispModuleSetBacklightLevel(sys_config.brightness);
				LogDebug("set backlight : %u\n", sys_config.brightness);
				CalculateDispModuleBacklightIndex(sys_config.brightness, &disp_module_backlight_index);
				is_brightness_changed_by_cmd = 0;
				is_brightness_changed_by_key = 0;
			} else if (is_brightness_changed_by_key) {
				sys_config.brightness = brightness_set_by_key;
				SysConfigStore(&sys_config);
				DispModuleSetBacklightLevel(sys_config.brightness);
				LogDebug("set backlight : %u\n", sys_config.brightness);
				is_brightness_changed_by_key = 0;
			}
		}
	}
}
